home *** CD-ROM | disk | FTP | other *** search
/ Mac-Source 1994 July / Mac-Source_July_1994.iso / C and C++ / Libraries / BlobMgr / Demo Folder / Radix.c < prev    next >
Encoding:
C/C++ Source or Header  |  1994-02-21  |  11.4 KB  |  498 lines  |  [TEXT/KAHL]

  1. /*
  2.  * Blob Manager Demonstration:  Arithmetic problem module
  3.  *
  4.  * Displays arithmetic problems to solve.  Allows radix to be set
  5.  * anywhere from base 2 to base 10.
  6.  *
  7.  * This module demonstrates a simple use of the BlobClick advisory
  8.  * function.
  9.  *
  10.  * 26 July 1986        Paul DuBois
  11.  */
  12.  
  13. # include    "TransSkel.h"
  14.  
  15. # include    "BlobMgr.h"
  16. # include    "BlobDemo.h"
  17.  
  18.  
  19. /*
  20.  * Blob types, used to distinguish different parts of problem
  21.  */
  22.  
  23. enum
  24. {
  25.     donorBlob,        /* for donor digits */
  26.     carryBlob,        /* for carry digits */
  27.     addendBlob,        /* for addend digits */
  28.     sumBlob,        /* for sum digits */
  29.     operBlob        /* for the plus sign */
  30. };
  31.  
  32.  
  33. # define    carryPos    10    /* horizontal position of carry digits */
  34.                             /* addend and sum pos's are relative to this */
  35. # define    digitPos    110    /* position of donor digits */
  36. # define    digitSize    18    /* size of digit blobs */
  37. # define    digitGap    2    /* gap between blobs */
  38. # define    absMaxLen    10    /* absolute max number of digits in addends */
  39.  
  40.  
  41. static WindowPtr        wind;
  42. static MenuHandle        radixMenu;
  43. static ControlHandle    checkAns;
  44. static ControlHandle    giveUp;
  45. static ControlHandle    nextProb;
  46.  
  47. static short            radix = 10;        /* initially decimal */
  48.  
  49. static BlobSetHandle    digits = nil;    /* donor blobs */
  50. static BlobSetHandle    problem = nil;    /* receptor blobs */
  51. static BlobHandle        firstAddendBlob;
  52. static BlobHandle        firstSumBlob;
  53. static BlobHandle        plusSignBlob;
  54. static short            maxLen = 6;        /* current max digits in addends */
  55. static short            rightEdge;        /* right edge of problem display */
  56. static short            lineX;            /* pos and length of line */
  57. static short            lineY;            /* between lower addend and sum */
  58. static short            lineLen;
  59.  
  60. static Boolean            paused;
  61.  
  62.  
  63. /*
  64.  * Pick a problem digit.  Don't pick zero if canBeZero is false.
  65.  */
  66.  
  67. static short
  68. PickDigit (Boolean canBeZero)
  69. {
  70. short    n;
  71.  
  72.     do {
  73.         n = BlobRand (radix-1);        /* use Blob Manager rand function */
  74.     } while (!canBeZero && n == 0);
  75.     return (n);
  76. }
  77.  
  78.  
  79. /*
  80.  * Make a blob.  Pass the x and y coordinates, the character drawn in the
  81.  * blob, whether it needs an explicit match or not.  Carry digits don't
  82.  * need an explicit match, digits of the sum do - except that the leftmost
  83.  * sum blob might be unnecessary - so it can be zero or nothing.
  84.  */
  85.  
  86. static BlobHandle
  87. MakeBlob (bSet, x, y, c, needMatch, type)
  88. BlobSetHandle    bSet;
  89. short            x, y;
  90. short            c;
  91. Boolean            needMatch;
  92. short            type;
  93. {
  94. BlobHandle        b;
  95.  
  96.     b = MakeCharBlob (bSet, false, infiniteGlue, needMatch,
  97.                 x, y, c);
  98.     SetBRefCon (b, c + ((long) type << 16));
  99.     return (b);
  100. }
  101.  
  102.  
  103. /*
  104.  * Return blob type, encoded as the high word of the reference
  105.  * constant.
  106. */
  107.  
  108. static short
  109. GetBlobType (BlobHandle b)
  110. {
  111.     return (HiWord (GetBRefCon (b)));
  112. }
  113.  
  114.  
  115. /*
  116.  * Generate a new problem to solve.  The addends are at least two digits
  117.  * long.  The max number of digits in the sum will be 1 greater than the
  118.  * longest addend.  The max number of carry digits is the same as the length
  119.  * of the longest addend.
  120.  *
  121.  * Digits in the addend and sum arrays are numbered from right to left.
  122.  */
  123.  
  124. static void
  125. GenerateProblem (void)
  126. {
  127. short        addend1[absMaxLen];
  128. short        addend2[absMaxLen];
  129. short        carry[absMaxLen];
  130. short        sum[absMaxLen+1];
  131. short        add1Len;            /* number of digits in addend 1 */
  132. short        add2Len;            /* number of digits in addend 2 */
  133. short        sumLen;                /* number of digits in sum */
  134. short        carryLen;            /* number of carry digits */
  135. BlobHandle    b;
  136. short        i;
  137. short        x, y;
  138.  
  139.     add1Len = BlobRand (maxLen - 2) + 2;    /* addends are at least 2 digits */
  140.     add2Len = BlobRand (maxLen - 2) + 2;
  141.     carryLen = (add1Len > add2Len ? add1Len : add2Len);
  142.     sumLen = carryLen + 1;
  143.  
  144.     /* zero the addends and the carry digits */
  145.  
  146.     for (i = 0; i < absMaxLen; ++i)
  147.     {
  148.         addend1[i] = 0;
  149.         addend2[i] = 0;
  150.         carry[i] = 0;
  151.     }
  152.  
  153.     /* generate digits for the addends and determine the sum. */
  154.     /* the leftmost digit of the addends should not be zero */
  155.  
  156.     for (i = 0; i < add1Len; ++i)
  157.         addend1[i] = PickDigit (i != add1Len - 1);
  158.     for (i = 0; i < add2Len; ++i)
  159.         addend2[i] = PickDigit (i != add2Len - 1);
  160.     for (i = 0; i < sumLen; ++i)
  161.     {
  162.         sum[i] = addend1[i] + addend2[i];
  163.         if (i > 0 && sum[i-1] >= radix)
  164.         {
  165.             sum[i-1] -= radix;
  166.             ++sum[i];
  167.             ++carry[i-1];
  168.         }
  169.     }
  170.  
  171.     /* get rid of any old problem blob set */
  172.  
  173.     if (problem != nil)
  174.     {
  175.         HideBlobSet (problem);
  176.         DisposeBlobSet (problem);
  177.     }
  178.     problem = NewBlobSet ();
  179.  
  180.     /*
  181.      * Generate blobs for carry digits - they don't need an explicit
  182.      * match.  Carry digits are placed over every addend digit
  183.      * except the rightmost
  184.      */
  185.  
  186.     x = rightEdge - digitSize;
  187.     y = carryPos;
  188.     for (i = 0; i < carryLen; ++i)
  189.     {
  190.         x -= digitSize + digitGap;
  191.         b = MakeBlob (problem, x, y, ' ', false, carryBlob);
  192.         NewBlobMatch (GetBlobHandle (digits, carry[i]), b);
  193.     }
  194.  
  195.     /*
  196.      * Generate blobs for addends.  addend blobs are given a non-zero
  197.      * reference value so the advisory function can distinguish them
  198.      * from carry and sum blobs easily.
  199.      */
  200.  
  201.     x = rightEdge - digitSize;
  202.     y += digitSize + digitGap;
  203.     for (i = 0; i < add1Len; ++i)
  204.     {
  205.         b = MakeBlob (problem, x, y, ' ', false, addendBlob);
  206.         GlueGlob (GetBlobHandle (digits, addend1[i]), b);
  207.         if (i == 0)
  208.             firstAddendBlob = b;
  209.         x -= digitSize + digitGap;
  210.     }
  211.  
  212.     x = rightEdge - digitSize;
  213.     y += digitSize + digitGap;
  214.     for (i = 0; i < add2Len; ++i)
  215.     {
  216.         b = MakeBlob (problem, x, y, ' ', false, addendBlob);
  217.         GlueGlob (GetBlobHandle (digits, addend2[i]), b);
  218.         x -= digitSize + digitGap;
  219.     }
  220.     
  221.     /* add the plus sign */
  222.  
  223.     plusSignBlob = MakeBlob (problem, x, y, '+', false, operBlob);
  224.     FreezeBlob (plusSignBlob);
  225.  
  226.     /* figure out length and position of line 'tween addend2 and sum */
  227.  
  228.     y += digitSize + digitGap;
  229.     lineLen = sumLen * (digitSize + digitGap) - digitGap;
  230.     lineX = rightEdge - lineLen;
  231.     lineY = y;
  232.  
  233.     /*
  234.      * Make sum digits.  These must be matched explicitly, except
  235.      * possibly the leftmost one.
  236.      */
  237.  
  238.     x = rightEdge - digitSize;
  239.     y += digitGap + 2;
  240.     for (i = 0; i < sumLen; ++i)
  241.     {
  242.         b = MakeBlob (problem, x, y, ' ', true, sumBlob);
  243.         if (i == sumLen - 1 && sum[i] == 0)
  244.             ClearBlobFlags (b, bNeedGlobMask);    /* explicit match unneeded */
  245.         NewBlobMatch (GetBlobHandle (digits, sum[i]), b);
  246.         if (i == 0)
  247.             firstSumBlob = b;
  248.         x -= digitSize + digitGap;
  249.     }
  250. }
  251.  
  252.  
  253. static void
  254. DrawLine (void)
  255. {
  256.     MoveTo (lineX, lineY);
  257.     LineTo (rightEdge, lineY);
  258. }
  259.  
  260.  
  261. static void
  262. NextProblem (void)
  263. {
  264.     InvalRect (&wind->portRect);
  265.     PenMode (patBic);
  266.     DrawLine ();                    /* erase line */
  267.     PenNormal ();
  268.     SetCTitle (checkAns, "\pCheck");
  269.     HiliteControl (checkAns, normalHilite);    /* make sure these buttons are on */
  270.     HiliteControl (giveUp, normalHilite);
  271.     paused = false;
  272.     /* choose numbers */
  273.     GenerateProblem ();
  274.     ShowBlobSet (problem);
  275.     DrawLine ();
  276.     ValidRect (&wind->portRect);
  277. }
  278.  
  279.  
  280. /*
  281.  * Make digit set - creates only the digits that are legal for the
  282.  * current radix.
  283.  */
  284.  
  285. static void
  286. MakeDigits (void)
  287. {
  288. short        i, x;
  289.  
  290.     if (digits != nil)
  291.     {
  292.         HideBlobSet (digits);
  293.         DisposeBlobSet (digits);
  294.     }
  295.     digits = NewBlobSet ();
  296.     x = rightEdge - radix * (digitSize + digitGap) + digitGap;
  297.     for (i = 0; i < radix; ++i)
  298.     {
  299.         (void) MakeBlob (digits, x, digitPos, i + '0', false, donorBlob);
  300.         x += digitSize + digitGap;
  301.     }
  302.     ShowBlobSet (digits);
  303. }
  304.  
  305.  
  306. /*
  307.  * Radix menu handler
  308.  *
  309.  * Change the radix and choose a new problem.
  310.  */
  311.  
  312. static pascal void
  313. ChooseRadix (short item)
  314. {
  315.     CheckItem (radixMenu, radix-1, false);
  316.     radix = item + 1;    /* menu items start with Base 2 */
  317.     CheckItem (radixMenu, radix-1, true);
  318.     MakeDigits ();
  319.     NextProblem ();
  320. }
  321.  
  322.  
  323. static pascal void
  324. Mouse (Point pt, long t, short mods)
  325. {
  326. BlobHandle        b;
  327. short            type;
  328. ControlHandle    ctl;
  329.  
  330.     if (FindControl    (pt, wind, &ctl))
  331.     {
  332.         if (TrackControl (ctl, pt, nil))    /* any button hit? */
  333.         {
  334.             if (ctl == nextProb)
  335.                 NextProblem ();
  336.             else if (ctl == checkAns)
  337.             {
  338.                 if (!paused)
  339.                 {
  340.                     /* give feedback (this freezes the receptors) */
  341.                     BlobFeedback (problem, normalDraw, dimDraw);
  342.                     SetCTitle (checkAns, "\pResume");
  343.                     paused = true;
  344.                 }
  345.                 else
  346.                 {
  347.                     /* allow user to continue working */
  348.                     SetCTitle (checkAns, "\pCheck");
  349.                     paused = false;
  350.                     ThawBlobSet (problem);        /* thaw entire problem */
  351.                     FreezeBlob (plusSignBlob);    /* except plus sign */
  352.                 }
  353.             }
  354.             else if (ctl == giveUp)
  355.             {
  356.             /*
  357.              * Show answer.  Show only the non-zero carry digits,
  358.              * and the sum digits.  Show the leftmost sum digit only
  359.              * if it's non-zero.  Have to that the problem first,
  360.              * because some of it may have been dimmed by Check.
  361.              */
  362.                 ThawBlobSet (problem);    /* might be frozen from Check */
  363.                 for (b = FirstBlob (problem); NextBlob (b) != nil; b = NextBlob (b))
  364.                 {
  365.                     type = GetBlobType (b);
  366.                     if (type == carryBlob)
  367.                     {
  368.                         UnglueGlob (b);    /* clear any glob it might have */
  369.                         if (FirstBMatch (b) == GetBlobHandle (digits, 1))
  370.                             ZGlueGlob (FirstBMatch (b), b);
  371.                     }
  372.                     else if (type == sumBlob)
  373.                         ZGlueGlob (FirstBMatch (b), b);
  374.                 }
  375.                 /*
  376.                  * for loop leaves b pointing at last blob in problem set,
  377.                  * i.e., the leftmost sum digit.
  378.                  */
  379.                 if (FirstBMatch (b) != GetBlobHandle (digits, 0))
  380.                     ZGlueGlob (FirstBMatch (b), b);
  381.  
  382.                 HiliteControl (checkAns, dimHilite);    /* make inactive */
  383.                 HiliteControl (giveUp, dimHilite);
  384.                 paused = true;
  385.             }
  386.         }
  387.     }
  388.     else if (!paused)
  389.     {
  390.         BlobClick (pt, t, digits, problem);
  391.         if (BlobSetQuiet (problem))        /* done yet? */
  392.         {
  393.             HiliteControl (checkAns, dimHilite);
  394.             HiliteControl (giveUp, dimHilite);
  395.             paused = true;
  396.         }
  397.     }
  398. }
  399.  
  400.  
  401. /*
  402.  * The purpose of the advisory is to allow digits from the addends
  403.  * to be duplicated onto carry or sum digits, but to prevent addend
  404.  * blobs from being cleared or duplicated onto, and to prevent donors
  405.  * from being glued to them.  So, whenever a clear, glue, or duplicate
  406.  * message if received, return false if the blob involved is an addend
  407.  * to make BlobClick abort.  Addends are easily distinguished since
  408.  * they have non-zero reference values.
  409.  *
  410.  * Transfer and swap messages will never be received since the
  411.  * permissions are set to disallow those transaction types.
  412.  * For any other message, return true to continue normal processing.
  413.  */
  414.  
  415. static pascal Boolean
  416. Advisory (short mesg, BlobHandle b)
  417. {
  418.     switch (mesg)
  419.     {
  420.         case advGlue:
  421.         case advUnglue:
  422.         case advDup:
  423.             if (GetBlobType (b) == addendBlob)
  424.                 return (false);
  425.     }
  426.     return (true);
  427. }
  428.  
  429.  
  430. static pascal void
  431. Update (Boolean resized)
  432. {
  433. GrafPtr    port;
  434.  
  435.     GetPort (&port);
  436.     EraseRect (&port->portRect);
  437.  
  438.     DrawControls (wind);
  439.     DrawBlobSet (problem);
  440.     DrawLine ();
  441.     DrawBlobSet (digits);
  442. }
  443.  
  444.  
  445. static pascal void
  446. Activate (Boolean active)
  447. {
  448.     if (active)
  449.     {
  450.         SetDragRects (wind);
  451.         SetBCPermissions (true, true, true, false, true);
  452.         SetBCAdvisory (Advisory);
  453.         radixMenu = GetMenu (radixMenuRes);
  454.         SkelMenu (radixMenu, ChooseRadix, DoMClobber, false, true);
  455.         CheckItem (radixMenu, radix-1, true);
  456.     }
  457.     else
  458.     {
  459.         SkelRmveMenu (radixMenu);    /* destroy handler */
  460.         SetBCAdvisory (nil);
  461.     }
  462. }
  463.  
  464.  
  465. void
  466. RadixInit (void)
  467. {
  468. Rect    r;
  469.  
  470.     SkelWindow (wind = GetDemoWind (radixWindRes),
  471.                 Mouse,            /* mouse clicks */
  472.                 nil,            /* key clicks */
  473.                 Update,            /* updates */
  474.                 Activate,        /* activate/deactivate events */
  475.                 nil,            /* close window */
  476.                 DoWClobber,        /* dispose of window */
  477.                 nil,            /* idle proc */
  478.                 false);            /* irrelevant, since no idle proc */
  479.  
  480.     SetRect (&r, 0, 25, 70, 45);
  481.     OffsetRect (&r, wind->portRect.right - 80, 0);
  482.     checkAns = NewControl (wind, &r, "\pCheck", true, 0, 0, 0,
  483.                             pushButProc, 0L);
  484.     OffsetRect (&r, 0, 30);
  485.     giveUp = NewControl (wind, &r, "\pUncle", true, 0, 0, 0,
  486.                             pushButProc, 0L);
  487.     OffsetRect (&r, 0, 30);
  488.     nextProb = NewControl (wind, &r, "\pNext", true, 0, 0, 0,
  489.                             pushButProc, 0L);
  490.  
  491.     rightEdge = wind->portRect.right - 100;
  492.     SetCharBlobSize (digitSize);
  493.     MakeDigits ();
  494.     NextProblem ();
  495.  
  496.     MakeFrontWind (wind);
  497. }
  498.